//    Copyright (C) 2013  <lugocreation@gmail.com>
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>
package org.jpgolay.gui.date;

import com.sun.java.swing.plaf.motif.MotifComboBoxUI;
import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;
import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;

public class DateComboBox extends JComboBox {

    class MetalDateComboBoxUI extends MetalComboBoxUI {

        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        MetalDateComboBoxUI() {
        }
    }

    class WindowsDateComboBoxUI extends WindowsComboBoxUI {

        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        WindowsDateComboBoxUI() {
        }
    }

    class MotifDateComboBoxUI extends MotifComboBoxUI {

        protected ComboPopup createPopup() {
            return new DatePopup(comboBox);
        }

        MotifDateComboBoxUI() {
        }
    }

    class DatePopup
            implements ComboPopup, MouseMotionListener, MouseListener, KeyListener, PopupMenuListener {

        public void show() {
            try {
                calendar.setTime(dateFormat.parse(comboBox.getSelectedItem().toString()));
            } catch (Exception exception) {
            }
            updatePopup();
            popup.show(comboBox, 0, comboBox.getHeight());
        }

        public void hide() {
            popup.setVisible(false);
        }

        public JList getList() {
            return list;
        }

        public MouseListener getMouseListener() {
            return this;
        }

        public MouseMotionListener getMouseMotionListener() {
            return this;
        }

        public KeyListener getKeyListener() {
            return this;
        }

        public boolean isVisible() {
            return popup.isVisible();
        }

        public void uninstallingUI() {
            popup.removePopupMenuListener(this);
        }

        public void mousePressed(MouseEvent mouseevent) {
        }

        public void mouseReleased(MouseEvent mouseevent) {
        }

        public void mouseClicked(MouseEvent e) {
            if (!SwingUtilities.isLeftMouseButton(e)) {
                return;
            }
            if (!comboBox.isEnabled()) {
                return;
            }
            if (comboBox.isEditable()) {
                comboBox.getEditor().getEditorComponent().requestFocus();
            } else {
                comboBox.requestFocus();
            }
            togglePopup();
        }

        public void mouseEntered(MouseEvent e) {
            mouseInside = true;
        }

        public void mouseExited(MouseEvent e) {
            mouseInside = false;
        }

        public void mouseDragged(MouseEvent mouseevent) {
        }

        public void mouseMoved(MouseEvent mouseevent) {
        }

        public void keyPressed(KeyEvent keyevent) {
        }

        public void keyTyped(KeyEvent keyevent) {
        }

        public void keyReleased(KeyEvent e) {
            if (e.getKeyCode() == 32 || e.getKeyCode() == 10) {
                togglePopup();
            }
        }

        public void popupMenuCanceled(PopupMenuEvent popupmenuevent) {
        }

        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            hideNext = mouseInside;
        }

        public void popupMenuWillBecomeVisible(PopupMenuEvent popupmenuevent) {
        }

        protected void togglePopup() {
            if (isVisible() || hideNext) {
                hide();
            } else {
                show();
            }
            hideNext = false;
        }

        protected JLabel createUpdateButton(final int field, final int amount) {
            final JLabel label = new JLabel();
            final Border selectedBorder = new EtchedBorder();
            final Border unselectedBorder = new EmptyBorder(selectedBorder.getBorderInsets(new JLabel()));
            label.setBorder(unselectedBorder);
            label.setForeground(foreground);
            label.addMouseListener(new MouseAdapter() {
                public void mouseReleased(MouseEvent e) {
                    calendar.add(field, amount);
                    updatePopup();
                }

                public void mouseEntered(MouseEvent e) {
                    label.setBorder(selectedBorder);
                }

                public void mouseExited(MouseEvent e) {
                    label.setBorder(unselectedBorder);
                }
            });
            return label;
        }

        protected void initializePopup() {
            JPanel header = new JPanel();
            header.setLayout(new BoxLayout(header, 0));
            header.setBackground(background);
            header.setOpaque(true);
            JLabel label = createUpdateButton(1, -1);
            label.setText("<<");
            label.setToolTipText("Ann\351e pr\351c\351dente");
            header.add(Box.createHorizontalStrut(12));
            header.add(label);
            header.add(Box.createHorizontalStrut(12));
            label = createUpdateButton(2, -1);
            label.setText("<");
            label.setToolTipText("Mois pr\351c\351dent");
            header.add(label);
            monthLabel = new JLabel("", 0);
            monthLabel.setForeground(foreground);
            header.add(Box.createHorizontalGlue());
            header.add(monthLabel);
            header.add(Box.createHorizontalGlue());
            label = createUpdateButton(2, 1);
            label.setText(">");
            label.setToolTipText("Mois suivant");
            header.add(label);
            label = createUpdateButton(1, 1);
            label.setText(">>");
            label.setToolTipText("Ann\351e suivante");
            header.add(Box.createHorizontalStrut(12));
            header.add(label);
            header.add(Box.createHorizontalStrut(12));
            popup = new JPopupMenu();
            popup.setBorder(BorderFactory.createLineBorder(Color.black));
            popup.setLayout(new BorderLayout());
            popup.setBackground(background);
            popup.addPopupMenuListener(this);
            popup.add("North", header);
        }

        protected void updatePopup() {
            monthLabel.setText(monthFormat.format(calendar.getTime()));
            if (days != null) {
                popup.remove(days);
            }
            days = new JPanel(new GridLayout(0, 7));
            days.setBackground(background);
            days.setOpaque(true);
            String dayOfTheWeek[] = {
                "D", "L", "M", "M", "J", "V", "S"
            };
            for (int i = 0; i < 7; i++) {
                JLabel label = new JLabel();
                label.setHorizontalAlignment(0);
                label.setForeground(foreground);
                label.setText(dayOfTheWeek[i]);
                days.add(label);
            }

            Calendar setupCalendar = (Calendar) calendar.clone();
            setupCalendar = (Calendar) calendar.clone();
            setupCalendar.set(5, 1);
            int first = setupCalendar.get(7);
            switch (first) {
                case 2: // '\002'
                    days.add(new JLabel(""));
                    break;

                case 3: // '\003'
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    break;

                case 4: // '\004'
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    break;

                case 5: // '\005'
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    break;

                case 6: // '\006'
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    break;

                case 7: // '\007'
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    days.add(new JLabel(""));
                    break;
            }
            for (int i = 1; i <= setupCalendar.getActualMaximum(5); i++) {
                final int day = i;
                final JLabel label = new JLabel(String.valueOf(day));
                label.setHorizontalAlignment(0);
                label.setForeground(foreground);
                label.addMouseListener(new MouseListener() {
                    public void mousePressed(MouseEvent mouseevent) {
                    }

                    public void mouseClicked(MouseEvent mouseevent) {
                    }

                    public void mouseReleased(MouseEvent e) {
                        label.setOpaque(false);
                        label.setBackground(background);
                        label.setForeground(foreground);
                        calendar.set(5, day);
                        comboBox.setSelectedItem(dateFormat.format(calendar.getTime()));
                        comboBox.requestFocus();
                    }

                    public void mouseEntered(MouseEvent e) {
                        label.setOpaque(true);
                        label.setBackground(selectedBackground);
                        label.setForeground(selectedForeground);
                    }

                    public void mouseExited(MouseEvent e) {
                        label.setOpaque(false);
                        label.setBackground(background);
                        label.setForeground(foreground);
                    }
                });
                days.add(label);
            }

            popup.add("Center", days);
            popup.pack();
        }
        protected JComboBox comboBox;
        protected Calendar calendar;
        protected JPopupMenu popup;
        protected JLabel monthLabel;
        protected JPanel days;
        protected SimpleDateFormat monthFormat;
        protected Color selectedBackground;
        protected Color selectedForeground;
        protected Color background;
        protected Color foreground;
        protected JList list;
        protected boolean mouseInside;
        protected boolean hideNext;

        public DatePopup(JComboBox comboBox) {
            days = null;
            monthFormat = new SimpleDateFormat("MMM  yyyy");
            list = new JList();
            mouseInside = false;
            hideNext = false;
            this.comboBox = comboBox;
            calendar = Calendar.getInstance();
            background = UIManager.getColor("ComboBox.background");
            foreground = UIManager.getColor("ComboBox.foreground");
            selectedBackground = UIManager.getColor("ComboBox.selectionBackground");
            selectedForeground = UIManager.getColor("ComboBox.selectionForeground");
            initializePopup();
        }
    }

    public DateComboBox() {
        dateFormat = new SimpleDateFormat("dd/MM/yyyy");
    }

    public void setDateFormat(SimpleDateFormat dateFormat) {
        this.dateFormat = dateFormat;
    }

    public void setSelectedItem(Object item) {
        removeAllItems();
        addItem(item);
        super.setSelectedItem(item);
    }

    public void updateUI() {
        ComboBoxUI cui = (ComboBoxUI) UIManager.getUI(this);
        if (cui instanceof MotifComboBoxUI) {
            cui = new MotifDateComboBoxUI();
        } else if (cui instanceof WindowsComboBoxUI) {
            cui = new WindowsDateComboBoxUI();
        } else {
            cui = new MetalDateComboBoxUI();
        }
        setUI(cui);
    }
    protected SimpleDateFormat dateFormat;
}
